﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Console;
using System.Collections.Generic;
using System.Linq;

namespace ExprParser
{
  /// <summary>
  /// Description of Pratt.
  /// </summary>
  public module Pratt
  {
    public Parse(tokens : List[Token]) : PExpr
    {
      mutable currentIndex = 0;
      def getLeftBindingPower(tok : Token, prefix : bool) : int
      {
        match (tok)
        {
          | Expr                  => 0
          | Operator   ("&&")     => 30
          | Operator   ("||")     => 30
          | Operator   ("+")      => 50
          | Operator   ("-")      => if (prefix) 100 else 50
          | Operator   ("*")      => 60
          | Operator   ("/")      => 60
          | Operator   (".")      => 80
          | Operator   (_)        => assert(false)
          | Eof                   => int.MinValue
        }
      }
      def next()
      {
        if (currentIndex < tokens.Count)
        {
          def tok = tokens[currentIndex];
          ++currentIndex;
          tok
        }
        else
          Token.Eof()
      }
      mutable token = next();
      def expression(rbp) : PExpr
      {
        mutable t     = token;
                token = next();
        mutable left  = nud(t);

        while (rbp < getLeftBindingPower(token, false))
        {
          t = token;
          token = next();
          left = led(left, t);
        }

        left;
      }
      and nud(tok : Token) : PExpr
      {
        | Operator   ("-")      => PExpr.PrefixOperator("-", expression(getLeftBindingPower(tok, true)))
        | Operator   (_)        => assert(false)
        | Expr(expr)            => expr
        | Eof                   => PExpr.Error("Expected simple expressoin.");  //assert(false)
      }
      and led(left : PExpr, tok : Token) : PExpr
      {
        match (tok)
        {
          | Operator   (op) => PExpr.BinOperator(left, op, expression(getLeftBindingPower(tok, false)))
          | _ => assert(false) 
        }
      }

      expression(0)
    }
  }
}
